﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Markup.Primitives;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using System.Xml;


namespace AnChao.WPF.Controls.MDI.Core
{
    /// <summary>
    /// 有关Xaml文件的辅助类
    /// </summary>
    public static class XamlHelper
    {
        /// <summary>
        /// 从xaml文件中 创建 xaml对象
        /// </summary>
        /// <param name="file">xaml文件的"地址"，./file.xaml</param>
        /// <returns>返回xaml文件定义的对象</returns>
        public static object LoadXamlFromFile(string file)
        {
            XmlReader reader = XmlReader.Create(file);
            return XamlReader.Load(reader);
        }

        /// <summary>
        /// 从文件创建xaml对象，该文件 的Build Action 为 "Resource"，被嵌入程序集内
        /// </summary>
        /// <param name="uri">xaml文件的"地址"，格式:"/命名空间;component/文件位置"</param>
        /// <returns>返回xaml文件定义的对象</returns>
        public static object LoadXamlFromResource(string uri)
        {
            Uri fileUri = new Uri(uri, UriKind.Relative);
            StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri);

            if (streamInfo != null && streamInfo.Stream != null)
            {
                using (StreamReader reader = new StreamReader(streamInfo.Stream))
                {
                    return XamlReader.Load(reader.BaseStream);
                }
            }

            return null;
        }

        /// <summary>
        /// 从文件创建xaml对象，该文件 的Build Action 为 "Resource"，被嵌入程序集内
        /// </summary>
        /// <param name="uri">xaml文件的"地址"，格式:"/命名空间;component/文件位置"</param>
        /// <returns>返回xaml文件定义的对象</returns>
        public static string GetXamlFromResource(string uri)
        {
            Uri fileUri = new Uri(uri, UriKind.Relative);
            StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri);

            if (streamInfo != null && streamInfo.Stream != null)
            {
                using (StreamReader reader = new StreamReader(streamInfo.Stream))
                {
                    return reader.ReadToEnd();
                }
            }

            return "";
        }


        /// <summary>
        /// 从文件创建xaml对象，该文件 的Build Action 为 "Page"，被嵌入程序集内
        /// </summary>
        /// <param name="uri">xaml文件的"地址"，格式:"/命名空间;component/文件位置"</param>
        /// <returns>返回xaml文件定义的对象</returns>
        public static object LoadBXamlFromResource(string uri)
        {
            Uri fileUri = new Uri(uri, UriKind.Relative);

            return Application.LoadComponent(fileUri);
        }

        /// <summary>
        /// 从字符串创建xaml对象
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static object CreateElementFromString(string s)
        {
            StringReader stringReader = new StringReader(s);
            XmlReader xmlReader = XmlReader.Create(stringReader, new XmlReaderSettings());
            return XamlReader.Load(xmlReader);
        }

        /// <summary>
        /// 将对象序列化成一个字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ConvertElementToString(object obj)
        {
            return XamlWriter.Save(obj);
        }


        /// <summary>
        /// 查找全局的资源
        /// 这些资源应该放在全局的ResourceDictionary中，并具有x:Key属性
        /// </summary>
        /// <param name="resourceKey">Xaml文件中的x:Key关键字</param>
        /// <returns></returns>
        public static object FindGlobalResource(string resourceKey)
        {
            return Application.Current.TryFindResource(resourceKey);
        }

        /// <summary>
        /// 查找全局的字符串资源
        /// 这些资源应该放在全局的ResourceDictionary中，并具有x:Key属性
        /// </summary>
        /// <param name="resourceKey">Xaml文件中的x:Key关键字</param>
        /// <returns></returns>
        public static string FindGlobalStringResource(string resourceKey)
        {
            object res = Application.Current.TryFindResource(resourceKey);

            if (res == null)
                return "";

            return res as string;
        }


        /// <summary>
        /// 从当前的Assembly定位Xaml文件路径
        /// </summary>
        /// <param name="partUri"></param>
        /// <param name="kind"></param>
        /// <returns></returns>
        public static Uri GetRelativeUri(string partUri, UriKind kind = UriKind.Relative)
        {
            string name = Assembly.GetCallingAssembly().GetName().Name;

            if (kind == UriKind.Relative)
            {
                if (!partUri.StartsWith("component/"))
                    partUri = "component/" + partUri;

                return new Uri(string.Format("/{0};{1}", name, partUri), kind);
            }


            if (partUri.ToLower().StartsWith("component"))
                return new Uri(string.Format("pack://application:,,,/{0};{1}", name, partUri), UriKind.Absolute);
            else
                return new Uri(string.Format("pack://application:,,,/{0};component{1}", name, partUri), UriKind.Absolute);

        }

        /// <summary>
        /// 从给定的Assembly定位Xaml文件路径
        /// </summary>
        /// <param name="assembly">指定的Assembly</param>
        /// <param name="partUri"></param>
        /// <param name="kind"></param>
        /// <returns></returns>
        public static Uri GetRelativeUri(Assembly assembly, string partUri, UriKind kind = UriKind.Relative)
        {
            if (assembly == null) return null;

            string name = assembly.GetName().Name;

            if (kind == UriKind.Relative)
            {
                if (!partUri.StartsWith("component/"))
                    partUri = "component/" + partUri;

                return new Uri(string.Format("/{0};{1}", name, partUri), kind);
            }


            if (partUri.ToLower().StartsWith("component"))
                return new Uri(string.Format("pack://application:,,,/{0};{1}", name, partUri), UriKind.Absolute);
            else
                return new Uri(string.Format("pack://application:,,,/{0};component{1}", name, partUri), UriKind.Absolute);
        }

        /// <summary>
        /// 从相对路径加载Image资源
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static ImageSource GetRelativeImageFromUri(string uri)
        {
            if (string.IsNullOrEmpty(uri))
                return null;

            uri = string.Format("pack://application:,,,/{0};component{1}", Assembly.GetCallingAssembly().GetName().Name, uri);

            ImageSourceConverter isc = new ImageSourceConverter();

            return isc.ConvertFromString(uri) as ImageSource;
        }

        /// <summary>
        /// 从绝对路径加载Image资源
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static ImageSource GetImageFromUri(string uri)
        {
            if (string.IsNullOrEmpty(uri))
                return null;

            ImageSourceConverter isc = new ImageSourceConverter();

            return isc.ConvertFromString(uri) as ImageSource;
        }

        /// <summary>
        /// 从指定的Assembly和相对路径加载Image资源
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static ImageSource GetRelativeImageFromUri(Assembly assembly, string uri)
        {
            if (assembly == null || string.IsNullOrEmpty(uri))
                return null;

            uri = string.Format("pack://application:,,,/{0};component{1}", assembly.GetName().Name, uri);

            ImageSourceConverter isc = new ImageSourceConverter();

            return isc.ConvertFromString(uri) as ImageSource;
        }


        /// <summary>
        /// 从嵌入资源（EmbeddedResource）中获取Image对象
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="uri">资源名称，格式为：命名空间.文件名，如CRootGUI.Main.View.exit.png。</param>
        /// <returns></returns>
        public static System.Drawing.Image GetEmbeddedImageFromAssembly(Assembly assembly, string uri)
        {
            if (assembly == null || string.IsNullOrEmpty(uri))
                return null;

            Stream file = assembly.GetManifestResourceStream(uri);
            if (file == null)
                return null;

            return System.Drawing.Image.FromStream(file);
        }

        /// <summary>
        /// 从资源（Resource）中获取Image对象
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static System.Drawing.Image GetResourceImageFromAssembly(string uri)
        {
            if (string.IsNullOrEmpty(uri))
                return null;

            StreamResourceInfo sri = Application.GetResourceStream(new Uri(uri));
            if (sri != null && sri.Stream != null)
                return new System.Drawing.Bitmap(sri.Stream);

            return null;
        }

        /// <summary>
        /// 从资源中获取Image对象
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static System.Drawing.Image GetResourceImageFromAssembly(Assembly assembly, string uri)
        {
            if (assembly == null || string.IsNullOrEmpty(uri))
                return null;

            ImageSource imgsource = GetRelativeImageFromUri(assembly, uri);
            if (imgsource == null)
                return null;

            return BitmapSourceToBitmap(imgsource as BitmapSource);
        }

        /// <summary>
        /// 复制Bitmap
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="cropX"></param>
        /// <param name="cropY"></param>
        /// <param name="cropWidth"></param>
        /// <param name="cropHeight"></param>
        /// <returns></returns>
        public static System.Drawing.Bitmap CopyBitmap(System.Drawing.Bitmap bitmap, int cropX, int cropY, int cropWidth, int cropHeight)
        {
            System.Drawing.Rectangle rect = new System.Drawing.Rectangle(cropX, cropY, cropWidth, cropHeight);
            System.Drawing.Bitmap cropped = bitmap.Clone(rect, bitmap.PixelFormat);
            return cropped;
        }

        /// <summary>
        /// 将BitmapSource转换成System.Drawing.Bitmap
        /// </summary>
        /// <param name="srs"></param>
        /// <returns></returns>
        public static System.Drawing.Bitmap BitmapSourceToBitmap(BitmapSource srs)
        {
            System.Drawing.Bitmap btm = null;
            int width = srs.PixelWidth;
            int height = srs.PixelHeight;
            int stride = width * ((srs.Format.BitsPerPixel + 7) / 8);

            byte[] bits = new byte[height * stride];
            srs.CopyPixels(bits, stride, 0);

            IntPtr ptr = Marshal.AllocHGlobal(bits.Length);
            Marshal.Copy(bits, 0, ptr, bits.Length);

            btm = new System.Drawing.Bitmap(width, height, stride, System.Drawing.Imaging.PixelFormat.Format32bppPArgb, ptr);

            //btm = CopyBitmap(btm, 0, 0, btm.Width, btm.Height);

            //Marshal.FreeHGlobal(ptr);

            return btm;
        }


        ///// <summary>
        ///// 将System.Drawing.Image转换成BitmapSource
        ///// </summary>
        ///// <param name="bitmap"></param>
        ///// <returns></returns>
        //public static ImageSource ConvertBitmapToBitmapSource(System.Drawing.Bitmap bitmap)
        //{
        //    using (bitmap)
        //    {
        //        System.Windows.Media.Imaging.BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
        //            bitmap.GetHbitmap(),
        //            IntPtr.Zero,
        //            Int32Rect.Empty,
        //            System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

        //        ImageSourceConverter isc = new ImageSourceConverter();
        //        return isc.ConvertFrom(bitmapSource) as ImageSource;
        //    }
        //}

    }

    public static class CRootXamlWriter
    {
        public static string Save(object obj)
        {
            StringBuilder sb = new StringBuilder();
            WriteObject(obj, sb, true);
            return sb.ToString();

        }
        private static void WriteObject(object obj, StringBuilder sb, bool isRoot)
        {
            WriteObjectWithKey(null, obj, sb, isRoot);
        }
        private static void WriteObject(object obj, StringBuilder sb)
        {
            WriteObjectWithKey(null, obj, sb, false);
        }
        private static void WriteObjectWithKey(object key, object obj, StringBuilder sb)
        {
            WriteObjectWithKey(key, obj, sb, false);
        }
        private static Dictionary<Type, string> contentProperties = new Dictionary<Type, string>();

        private static void WriteObjectWithKey(object key, object obj, StringBuilder sb, bool isRoot)
        {
            List<MarkupProperty> propertyElements = new List<MarkupProperty>();
            //If the value is a string 
            string s = obj as string;
            if (s != null)
            {
                //TODO: in a dictionary, this should be serialized as a <s:String /> 
                sb.Append(s);
                return;
            }
            MarkupProperty contentProperty = null;
            string contentPropertyName = null;
            MarkupObject markupObj = MarkupWriter.GetMarkupObjectFor(obj);
            Type objectType = obj.GetType();
            sb.Append("<" + markupObj.ObjectType.Name);
            if (isRoot)
            {
                sb.Append(" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"");
            }
            if (key != null)
            {
                string keyString = key.ToString();
                if (keyString.Length > 0)
                    sb.Append(" x:Key=\"" + keyString + "\"");
                else
                    //TODO: key may not be a string, what about x:Type... 
                    throw new NotImplementedException("Sample XamlWriter cannot yet handle keys that aren't strings");
            }
            //Look for CPA info in our cache that keeps contentProperty names per Type 
            //If it doesn't have an entry, go get the info and store it. 
            if (!contentProperties.ContainsKey(objectType))
            {
                string lookedUpContentProperty = string.Empty;

                foreach (Attribute attr in markupObj.Attributes)
                {
                    ContentPropertyAttribute cpa = attr as ContentPropertyAttribute;
                    if (cpa != null)
                        lookedUpContentProperty = cpa.Name;
                }
                contentProperties.Add(objectType, lookedUpContentProperty);
            }
            contentPropertyName = contentProperties[objectType];
            string contentString = string.Empty;
            foreach (MarkupProperty markupProperty in markupObj.Properties)
            {
                if (markupProperty.Name != contentPropertyName)
                {
                    try
                    {
                        #region
                        if (markupProperty.IsValueAsString)
                        {
                            contentString = markupProperty.Value as string;
                        }

                        else if (!markupProperty.IsComposite)
                        {
                            if (null == markupProperty.Value)
                            {
                                sb.Append(" " + markupProperty.Name + "=\"{x:Null}\"");
                            }
                            else
                            {
                                sb.Append(" " + markupProperty.Name + "=\"" + markupProperty.Value + "\"");
                            }
                        }
                        else if (markupProperty.Value.GetType() == typeof(NullExtension))
                        {
                            sb.Append(" " + markupProperty.Name + "=\"{x:Null}\"");
                        }
                        else
                        {
                            propertyElements.Add(markupProperty);
                        }
                        #endregion
                    }
                    catch (Exception err)
                    {
                        Console.WriteLine(err);
                        string xx = err.Message;
                    }
                }
                else
                    contentProperty = markupProperty;
            }
            if (contentProperty != null || propertyElements.Count > 0 || contentString != string.Empty)
            {
                sb.Append(">");
                foreach (MarkupProperty markupProp in propertyElements)
                {
                    string propElementName = markupObj.ObjectType.Name + "." + markupProp.Name;
                    sb.Append("<" + propElementName + ">");
                    WriteChildren(sb, markupProp);
                    sb.Append("</" + propElementName + ">");
                }
                if (contentString != string.Empty)
                    sb.Append(contentString);
                else if (contentProperty != null)
                    WriteChildren(sb, contentProperty);
                sb.Append("</" + markupObj.ObjectType.Name + ">");
            }
            else
            {
                sb.Append("/>");
            }
        }

        private static void WriteChildren(StringBuilder sb, MarkupProperty markupProp)
        {
            if (!markupProp.IsComposite)
            {
                WriteObject(markupProp.Value, sb);
            }
            else
            {
                IList collection = markupProp.Value as IList;
                IDictionary dictionary = markupProp.Value as IDictionary;
                if (collection != null)
                {
                    foreach (object o in collection)
                        WriteObject(o, sb);
                }

                else if (dictionary != null)
                {
                    foreach (object key in dictionary.Keys)
                    {
                        WriteObjectWithKey(key, dictionary[key], sb);
                    }
                }
                else
                {
                    WriteObject(markupProp.Value, sb);
                }
            }
        }
    }

}
